Skip to content

feat(costs): add generic custodial stored-value accounts#140

Draft
JaeLeex wants to merge 2 commits into
mainfrom
agent/custodial-stored-value-accounts
Draft

feat(costs): add generic custodial stored-value accounts#140
JaeLeex wants to merge 2 commits into
mainfrom
agent/custodial-stored-value-accounts

Conversation

@JaeLeex

@JaeLeex JaeLeex commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What changed

Adds the generic nunchi-costs module for account-scoped metering and opt-in custodial stored value.

  • programmatic account creation and lifecycle controls
  • global and account-scoped backend writer authority
  • rate cards, deterministic quotes, reservations, settlement, and suspension
  • immutable paid and expiring grant lots with grant-first allocation
  • support-mediated unused paid-lot refunds to the originating rail
  • idempotency, append-only journal mutations, and projection-ready finality facts

Why grants are separate from paid credits

This is a provenance split, not a second currency: both are credits and both can pay for usage, but they represent different economic claims.

  • Paid credits are purchased value. Each lot keeps its payment-rail reference, purchase terms, and amount; unused, unreserved value can be refunded only to that original rail.
  • Grants are plan entitlements, trials, promotions, or goodwill. Each lot keeps its reason, period, approval/audit references, and expiry; it is non-refundable.

Keeping only one aggregate balance would make it impossible to determine what may be refunded, what must expire, or why a particular credit was issued and consumed. It could also cause promotional value to be treated as refundable paid value.

Flow

  1. The Billing writer records a payment as a paid lot; the Adjustment writer records an entitlement or promotion as a grant lot.
  2. Both lots contribute to the account's spendable credits, while retaining their own provenance and rules.
  3. The Ingest writer reserves or records usage. Allocation is deterministic: unexpired grants are consumed first (earliest expiry first), then paid lots.
  4. A reservation holds the exact selected lots until it settles, releases, or expires. A refund can only draw from unused paid lots that are not reserved.
  5. Once the chain transaction is final, an idempotent finality event carries the exact top-up, grant, allocation, or refund to downstream projections and accounting.

This preserves the customer-facing experience of one spendable credit pool while keeping refund, expiry, authorization, and accounting rules correct and auditable.

Scope boundary

This PR contains reusable SDK primitives only.

The existing aggregate API remains available for metering compatibility. The stored-value path is opt-in and versioned.

Validation

  • cargo fmt --package nunchi-costs -- --check
  • cargo test -p nunchi-costs --all-features — 25 tests pass
  • cargo clippy -p nunchi-costs --all-features -- -D warnings
  • public-content scan and high-signal secret scan
  • git diff --check origin/main...HEAD

Requested review

Please focus on the account-core API, writer authority boundaries, serialization compatibility, grant-first allocation, reservation invariants, and paid-lot refund rules.

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

@JaeLeex
JaeLeex requested a review from faddat July 20, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant